﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace library
{
    class p2pResponse
    {
        p2pRequest Request;

        p2pResponse(p2pRequest request)
        {
            Request = request;
        }

        internal static void Process(object o)
        {
            p2pResponse res = new p2pResponse((p2pRequest)o);

            res.Process();
        }

        void Process()
        {
            switch (Request.Header.Command)
            {
                case RequestCommand.Peer:
                    ProcessPeer();
                    break;

                case RequestCommand.Data:
                    ProcessData();
                    break;

                case RequestCommand.Metadata:
                    ProcessMetadata();
                    break;
            }
        }

        void ProcessPeer()
        {
            if (Client.LocalPeer.EndPoint.Address.Equals(IPAddress.Any))
                Client.LocalPeer = Request.Header.Peer;

            IEnumerable<byte> data = Request.Data.Skip(Parameters.requestHeaderSize);

            if (data.Any() && data.Count() != Parameters.addressSize)
            {
                Request.TargetPeer.EndGetPeer();

                Peers.AddPeersFromBytes(data);
            }
            else
            {
                byte[] address = Request.Data.Skip(Parameters.addressSize).ToArray();

                List<Peer> peers = Peers.GetPeers(
                    closestToAddress: address,
                    excludeOriginAddress: address,
                    excludeOriginPeer: new[] { Request.TargetPeer },
                    command: RequestCommand.None).ToList();

                //todo: limitar quantidade de peers

                if (address.Count() == 0)
                    peers.Insert(0, Client.LocalPeer);

                p2pRequest.Enqueue(
                    Request.TargetPeer,
                    targetPeer: Request.OriginPeer,
                    data: Peers.ToBytes(peers),
                    command: RequestCommand.Peer,
                    returnPeer: Request.TargetPeer);
            }
        }

        void ProcessData()
        {
            byte[] address = Request.Data.Skip(Parameters.requestHeaderSize).Take(Parameters.addressSize).ToArray();

            byte[] data = Request.Data.Skip(Parameters.requestHeaderSize + Parameters.addressSize).ToArray();

            if (data.Count() > 0)
            {
                LocalPackets.Add(address, data, Request.TargetPeer);
            }
            else
            {
                data = LocalPackets.Get(address);

                if (data == null)
                {
                    p2pRequest.Enqueue(
                        originPeer: Request.OriginPeer,
                        address: address,
                        returnPeer: Request.Header.Peer ?? Request.TargetPeer);
                }
                else
                {
                    p2pRequest.Enqueue(
                        originPeer: Request.OriginPeer,
                        targetPeer: Request.Header.Peer ?? Request.TargetPeer,
                        address: address,
                        data: data);
                }
            }
        }

        void ProcessMetadata()
        {
            //byte[] address = Utils.ReadBytes(Request.Data, Parameters.requestHeaderSize);

            //byte[] data = Utils.ReadBytes(Request.Data, Parameters.requestHeaderSize + 4 + address.Length).ToArray();

            //if (data.Count() == 0)
            //{
            //    ValueHits addresses = LocalIndex.SearchMetadataAddressesByValue(address);

            //    if (addresses == null || !addresses.Any())
            //    {
            //        p2pRequest.Enqueue(
            //            originPeer: Request.OriginPeer,
            //            address: BitConverter.GetBytes(address.Length).Concat(address).ToArray(),
            //            command: RequestCommand.Metadata,
            //            returnPeer: Request.Header.Peer);
            //    }
            //    else
            //    {
            //        foreach (ValueHitsItem item in addresses)
            //        {
            //            p2pRequest.Enqueue(
            //                originPeer: Request.OriginPeer,
            //                targetPeer: Request.Header.Peer ?? Request.TargetPeer,
            //                address: item.Value,
            //                data: LocalPackets.Get(item.Value));
            //        }
            //    }
            //}
            //else
            //{
            //    LocalPackets.Add(Utils.GetAddress(), data, Request.TargetPeer);
            //}
        }
    }
}